/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cestum.cave.presenter.wave
{
	import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
	import org.waveprotocol.wave.model.document.operation.Attributes;
	import org.waveprotocol.wave.model.document.operation.AttributesUpdate;
	import org.waveprotocol.wave.model.document.operation.DocumentOperationCursor;
	import org.waveprotocol.wave.protocol.AnnotationBoundary;
	import org.waveprotocol.wave.protocol.AnnotationBoundaryBuilder;
	import org.waveprotocol.wave.protocol.Component;
	import org.waveprotocol.wave.protocol.ComponentBuilder;
	import org.waveprotocol.wave.protocol.ElementStart;
	import org.waveprotocol.wave.protocol.ElementStartBuilder;
	import org.waveprotocol.wave.protocol.KeyValuePair;
	import org.waveprotocol.wave.protocol.KeyValueUpdate;
	import org.waveprotocol.wave.protocol.KeyValueUpdateBuilder;
	import org.waveprotocol.wave.protocol.ProtocolDocumentOperationBuilder;
	import org.waveprotocol.wave.protocol.ReplaceAttributes;
	import org.waveprotocol.wave.protocol.ReplaceAttributesBuilder;
	import org.waveprotocol.wave.protocol.UpdateAttributes;
	import org.waveprotocol.wave.protocol.UpdateAttributesBuilder;
	
	public class InputOpDocCursor implements DocumentOperationCursor
	{
		var output:ProtocolDocumentOperationBuilder;
		public function InputOpDocCursor(_output:ProtocolDocumentOperationBuilder)
		{
			this.output = _output;
		}
		
		private function newComponentBuilder():ComponentBuilder {
			return Component.newBuilder();
		}
		
		public function retain(itemCount:int):void {
			output.addComponent(newComponentBuilder().setRetainItemCount(itemCount).build() as Component);
		}
		
		public function characters(characters:String):void {
			output.addComponent(newComponentBuilder().setCharacters(characters).build() as Component);
		}
		
		public function deleteCharacters(characters:String):void {
			output.addComponent(newComponentBuilder().setDeleteCharacters(characters).build() as Component);
		}
		
		public function elementStart(type:String, attributes:Attributes):void {
			var e:ElementStart= makeElementStart(type, attributes);
			output.addComponent(newComponentBuilder().setElementStart(e).build() as Component);
		}
		
		public function deleteElementStart(type:String, attributes:Attributes):void {
			var e:ElementStart= makeElementStart(type, attributes);
			output.addComponent(newComponentBuilder().setDeleteElementStart(e).build() as Component);
		}
		
		private function makeElementStart(
			type:String, attributes:Attributes):ElementStart {
			var e:ElementStartBuilder =ElementStart.newBuilder();			
			e.setType(type);
			var size:int = attributes.keySet().length;
			for (var i:int =0 ; i< size; i++) {
				var name:String = attributes.keySet()[i];
				e.addAttribute(KeyValuePair.newBuilder().setKey(name).setValue(attributes.getValue(name)).build() as KeyValuePair);
			}
			
			return ElementStart(e.build());
		}
		
		public function elementEnd():void {
			output.addComponent(newComponentBuilder().setElementEnd(true).build() as Component);
		}
		
		public function deleteElementEnd():void {
			output.addComponent(newComponentBuilder().setDeleteElementEnd(true).build() as Component);
		}
		
		//TODO Untested
		public function replaceAttributes(oldAttributes:Attributes, newAttributes:Attributes):void {
			var r:ReplaceAttributesBuilder = ReplaceAttributes.newBuilder();
			
			if (oldAttributes.isEmpty() && newAttributes.isEmpty()) {
				r.setEmpty(true);
			} else {
				var size1:int = oldAttributes.keySet().length;
				for (var x1:int =0; x1 <size1; x1++) {
					var name:String = oldAttributes.keySet()[x1];
					r.addOldAttribute(KeyValuePair.newBuilder()
						.setKey(name).setValue(oldAttributes.getValue(name)).build() as KeyValuePair);
				}
				
				var size2:int = newAttributes.keySet().length;
				for (var x2:int =0; x2 <size2; x2++) {
					var name:String = newAttributes.keySet()[x2];
					r.addNewAttribute(KeyValuePair.newBuilder().setKey(name).setValue(newAttributes.getValue(name)).build() as KeyValuePair);
				}
			}
			output.addComponent(newComponentBuilder().setReplaceAttributes(ReplaceAttributes(r.build())).build() as Component);

		}
		
		public function updateAttributes(attributes:AttributesUpdate):void {
			var  u:UpdateAttributesBuilder = UpdateAttributes.newBuilder();
			
			if (attributes.changeSize() == 0) {
				u.setEmpty(true);
			} else {
				for (var i:int = 0; i < attributes.changeSize(); i++) {
					u.addAttributeUpdate(makeKeyValueUpdate(
						attributes.getChangeKey(i), attributes.getOldValue(i), attributes.getNewValue(i)));
				}
			}
			output.addComponent(newComponentBuilder().setUpdateAttributes(u.build() as UpdateAttributes).build() as Component);
		}
		
		public function annotationBoundary(map:AnnotationBoundaryMap):void {
			var a:AnnotationBoundaryBuilder = AnnotationBoundary.newBuilder();
			
			if (map.endSize() == 0 && map.changeSize() == 0) {
				a.setEmpty(true);
			} else {
				for (var i:int = 0; i < map.endSize(); i++) {
					a.addEnd(map.getEndKey(i));
				}
				for (var i1:int = 0; i1 < map.changeSize(); i1++) {
					a.addChange(makeKeyValueUpdate(
						map.getChangeKey(i1), map.getOldValue(i1), map.getNewValue(i1)));
				}
			}
			
			output.addComponent(newComponentBuilder().setAnnotationBoundary(a.build() as AnnotationBoundary).build() as Component);
			
		}
		
		private function  makeKeyValueUpdate(key:String, oldValue:String, newValue:String):KeyValueUpdate {
				var  kvu:KeyValueUpdateBuilder = KeyValueUpdate.newBuilder();
				kvu.setKey(key);
				if (oldValue != null) {
					kvu.setOldValue(oldValue);
				}
				if (newValue != null) {
					kvu.setNewValue(newValue);
				}
				
				return KeyValueUpdate(kvu.build());
			}
	}
}